/*   Copyright (C) 2008  Jeroen v. Schagen & Youri Lima
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;

namespace Engine.Math
{
    /// <summary>
    ///     Represents a plane in a three-dimensional space.
    /// 
    ///     A plane is defined by the following formula:
    ///     Ax + By + Cz + D = 0
    /// 
    ///     This equates to a vector (the normal of the plane, whose x, y
    ///     and z components equate to the coefficients A, B and C
    ///     respectively), and a constant (D) which is the distance along
    ///     the normal you have to go to move the plane back to the origin.
    /// </summary>
    public class Plane
    {
        public float d;
        public Vector3 normal;

        # region Constructor

        /// <summary>
        ///     Default no-arg constructor.
        /// </summary>
        public Plane()
        {
            this.d = 0.0f;
            this.normal = Vector3.ZERO; 
        }

        /// <summary>
        ///     Construct plane using a plane object.
        /// </summary>
        /// 
        /// <param name="plane"></param>
        public Plane(Plane plane)
        {
            this.d = plane.d;
            this.normal = plane.normal;
        }

        /// <summary>
        ///     Construct plane using a vector and constant.
        /// </summary>
        /// 
        /// <param name="normal">Normal vector</param>
        /// <param name="d">Constant</param>
        public Plane(Vector3 normal, float d)
        {
            this.d = -d;
            this.normal = normal;
        }

        /// <summary>
        ///     Construct plane using two points.
        /// </summary>
        /// 
        /// <param name="normal">Normal vector</param>
        /// <param name="point">Point vector</param>
        public Plane(Vector3 normal, Vector3 point)
        {
            this.d = -normal.DotProduct(point);
            this.normal = normal;
        }

        /// <summary>
        ///     Construct plane using three points.
        /// </summary>
        /// 
        /// <param name="point1">Point #1 vector</param>
        /// <param name="point2">Point #2 vector</param>
        /// <param name="point3">Point #3 vector</param>
        public Plane(Vector3 point1, Vector3 point2, Vector3 point3)
        {
            normal = (point2 - point1).CrossProduct(point3 - point1);
            normal.Normalise();

            d = -normal.DotProduct(point1);
        }

        # endregion


        # region Function

        /// <summary>
        ///     Normalise plane.
        /// </summary>
        /// 
        /// <returns></returns>
        public float Normalise()
        {
            float length = normal.Length;

            // Will also work for zero-sized vectors, but will change nothing
            if (length > 1e-08f)
            {
                float ilength = 1.0f / length;

                this.normal *= ilength;
                this.d *= ilength;
            }

            return length;
        }

        /// <summary>
        ///     Calculates the distance to a point.
        /// </summary>
        /// 
        /// <param name="v">Point</param>
        /// <returns>Distance</returns>
        public float Distance(Vector3 v)
        {
            return normal.DotProduct(v) + d;
        }

        /// <summary>
        ///     Project the plane on a vector.
        ///     TODO: Unfinished (check ogre)
        /// </summary>
        /// 
        /// <param name="v">Point</param>
        /// <returns>Projection</returns>
        public Vector3 Project(Vector3 v)
        {
            return Vector3.ZERO;
        }

        # endregion

        public override string ToString()
        {
            return "d: " + d + ", normal: " + normal.ToString();
        }
    }
}
